/*
	TODO UPGRADE vers LIBPERSEUS
*/

#include "mod_perseus.h"
#include "math.h"

#define alea() \
  (rand()/(RAND_MAX + 1.0))

/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*                                    String transformation                                          */


char *
int_to_char(request_rec *req,
            unsigned long * aTab,
            unsigned long aLength,
            unsigned int k,
            unsigned int N,
            unsigned long long *size)
{
  unsigned long long i, n, j;
  unsigned char r;
  char * data;

  aLength=aLength-N;
  *size=ceil((float) (aLength*k/8.0));
  data = (char *) apr_pcalloc(req->pool,sizeof(char)*(*size));

  if (k==8) {
      for(i=0; i<*size;i++)
        data[i]=aTab[i];
  }
  else
    {
      i=0;
      j=0;
      n=0;
      while(1)
        {

          for(r=0;r<8;r++) {
              if(i==k) {
                  if(++n==aLength) {
                      data[++j]='\0';
                      return data;
                  }
                  i=0;
              }
             data[j] |= ((aTab[n]>>(i)) &0x1)<<r;
              i++;
          }
          j++;
        }
    }
  data[*size]='\0';
  return data;
}

void
hex_to_Binchar(request_rec *r,
        const char * aDataHex,
        unsigned long aLength,
        char ** aDataBin,
        unsigned long long * aDataBinLength)
{
    unsigned long i;
    unsigned char j;
    unsigned char tmp;
    unsigned char rest=aDataHex[0] -'0';
    *aDataBinLength=0;
    
    if (aLength <= 1)
    {
        *aDataBin=NULL;
        return;
    }
    
    *aDataBin=(char *) apr_palloc(r->pool,sizeof(char)*((aLength-1)*4));
    for(i=1;i<aLength-1;i++) {
        if ('0' <= aDataHex[i] && aDataHex[i]<= '9') {
            tmp=aDataHex[i]-'0';
        } else if ( 'A'<= aDataHex[i] && aDataHex[i]<= 'F') {
            tmp=aDataHex[i] - 'A'+ 10;
        }
        for(j=0;j<4;j++) 
            (*aDataBin)[(*aDataBinLength)++]= (tmp>>(3-j)) &0x1;
    }

    /*Last Hex symbol */
    if ('0' <= aDataHex[aLength-1] && aDataHex[aLength-1]<= '9') {
        tmp=aDataHex[aLength-1]-'0';
    } else if ( 'A'<= aDataHex[aLength-1] && aDataHex[aLength-1]<= 'F') {
        tmp=aDataHex[aLength-1] - 'A'+ 10;
    }
    for(j=0;j<(4-rest);j++) 
            (*aDataBin)[(*aDataBinLength)++]= (tmp>>(3-j)) &0x1;

       
}

void
char_to_bin(
        request_rec *req,
        const char * aData,
        unsigned long long aLength,
        unsigned char aK,
        unsigned char aN,
        char ** aDataBin,
        unsigned long long * aNewLength
        )
{

    unsigned char Length_car=8;
    unsigned char j;
    unsigned long long i;
    unsigned long long k=0;
    unsigned int l;
    unsigned int bour= (aK - ((aLength*Length_car) % aK)) % aK;
    bour= bour +(aK*aN);
    *aDataBin= (char *) apr_palloc(req->pool,sizeof(char) * (aLength*Length_car)+bour);
    if (aDataBin == NULL)
        return;
    
    for(i=0;i<aLength;i++) {
        for(j=0;j<Length_car;j++)
            (*aDataBin)[k++]= (aData[i] >> j) &0x01;
    }
    for(l=0;l<bour;l++)
        (*aDataBin)[k++]=0;
    *aNewLength=k;
}

/*hex to char*/
/* return -1 error*/
char 
htoc(unsigned char hex)
{
    if ( hex>=0 && hex<=9)
        return hex+'0';
    if ( hex>9 && hex<=15)
        return hex+'A'-10;
    return -1;
}

void
bin_to_charHex(request_rec *req,
                char * aBinData,
               unsigned long long aLength,
               char ** aHexData,
               unsigned long long* aHexDataLength)
{
    unsigned long long i,k;
    unsigned char j=0,tmp=0;
//    char * oct= (char *)  apr_pcalloc(req->pool,sizeof(char)*2);
  //  unsigned char mod = aLength % 4;

    *aHexDataLength=0;
    *aHexData = (char *) apr_pcalloc(req->pool,sizeof(char)*((aLength/4)+1+1+1));

    k=1;

    for(i=0;i<aLength;i++) {
        tmp<<=1;
        tmp|=aBinData[i]&1;
        j++;
        if (j==4) {
            j=0;
            //apr_snprintf(oct,2,"%X",tmp);
            (*aHexData)[k]=htoc(tmp);
            k++;
            tmp=0;
        }   
    }
    if (j==0) {
        (*aHexData)[0]='0';
    }
    else {
        (*aHexData)[0]='0'+(4-j);
        while (j < 4) {
            j++;
            tmp<<=1;
        }
        //apr_snprintf(oct,2,"%X",tmp);
        (*aHexData)[k]=htoc(tmp);
        k++;
    }
    (*aHexData)[k]='\0';
    *aHexDataLength=k;

}


/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*                                    Noise function                                                 */

unsigned char noise(unsigned long long aReg, unsigned char aNumLine)
{
   unsigned long long index[10] = { 0x8A914C0804621A14ULL ,
      0x914A014085241543ULL ,
      0xA04281A49A023510ULL,
      0x104850915C10982CULL  ,
      0x4485A04212305138ULL ,
      0x31A0482C81234508ULL ,
      0x19024D0410C123A1ULL ,
      0x84A21115C6140105ULL ,
      0x4502618C13156840ULL ,
      0x3241C2034898C141ULL};
   
   /* 0 <= I <= 63 */
    unsigned char I = (aReg>>33) & 0x3F;
    return ((index[aNumLine]>>I) & 0x1);
}

unsigned char
sumbit(unsigned long long w)
{
    w ^= w>>32;
    w ^= w>>16;
    w ^= w>>8;
    w ^= w>>4;
    w ^= w>>2;
    w ^= w>>1;
    return (w & 0x1);
}

unsigned long long
_random(unsigned long long aPol, unsigned long long aReg)
{
    unsigned char rebouclage;
    unsigned char i;
    unsigned long long tmp;
    for(i=0;i<64; i++) {
        rebouclage = sumbit(aReg & aPol);
        aReg >>= 1;
        tmp = rebouclage;
        aReg |= tmp<<63;
    }
    return aReg;
}

void
addNoise(unsigned long long X0, unsigned char numLine,char * aData, unsigned long long aLength)
{
    unsigned long long reg=X0;
    unsigned long long P =  0x800000000000FFF9ULL;
    unsigned char Et;
    unsigned long long i=0,r=0;

    for(i=0;i<aLength;i++) {
        reg = _random(P,reg);
        Et = noise(reg,numLine);
        aData[i]^=Et;
        r++;
    }
}


/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*                                    PCC function                                              */
unsigned int
pcc_getN(pcc * p)
{
    return p->mN;
}

unsigned int
pcc_getK(pcc * p)
{
    return p->mK;
}

unsigned int
pcc_getM(pcc * p)
{
    return p->mM;
}

unsigned long **
pcc_getPoly(pcc * p)
{
    return p->mPoly;
}

unsigned int
pcc_getMatWidth(pcc * p)
{
    return p->mMatWidth;
}

unsigned char *
pcc_getMatrix(pcc * p)
{
    return p->mMatrix;
}

unsigned char
pcc_getNumLine(pcc * p)
{
    return p->mNumLine;
}

unsigned long long 
pcc_getX0(pcc * p)
{
    return p->mX0;
}

void
compute(unsigned int mN,
        unsigned int mM,
        unsigned long * aPoly,
        unsigned char aBit,
        unsigned long aMemory,
        unsigned int *value)
{
  unsigned int i,j;
  unsigned char pol;
  unsigned int val=0;
  for(i=0;i<mN;i++) {
      pol=aPoly[i]&1;
      val=pol&aBit;
      for(j=1;j<=mM;++j) {
          pol=(aPoly[i]>>j)&1;
          val^=pol&(aMemory>>(j-1));
      }
      *value<<=1;
      *value|=val;
  }
}


void 
pcc_puncture(request_rec *req, pcc *p,char * aStream, unsigned long long aLength,
            char ** aNewStream, unsigned long long *aNewLength)
{

    unsigned long long i;
    unsigned char j=0;
    *aNewLength=0;
    *aNewStream=(char *) apr_palloc(req->pool,sizeof(char) * aLength);

    for(i=0;i<aLength-1;i++) {
        if (j==(p->mMatWidth)*(p->mN)) {
            j=0;
        }
        if((p->mMatrix)[j]) {
            (*aNewStream)[*aNewLength]=aStream[i];
            (*aNewLength)++;
        }
        j++;
    }
    (*aNewStream)[*aNewLength]=aStream[aLength-1];
    (*aNewLength)++;

}

void
pcc_unpuncture(request_rec *req, pcc *p,char * aStream, unsigned long long aLength,
        char ** aNewStream, unsigned long long *aNewLength)
{

    unsigned long long i;
    unsigned char j=0;
    unsigned long long taille=(aLength+(aLength/(p->mN*p->mMatWidth-p->mMatDepth)+1)*p->mMatDepth);
    *aNewLength=0;
    *aNewStream=(char *)  apr_palloc(req->pool,sizeof(char) *taille);
    i=0;
    while(i<aLength) {
        if (j==p->mMatWidth*p->mN) {
            j=0;
        }
        if(! ((p->mMatrix)[j]) )
            (*aNewStream)[*aNewLength]=0xF;
        else {
            (*aNewStream)[*aNewLength]=aStream[i++];
        }
        (*aNewLength)++;
        j++;
    }
}



/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*                                    Position function                                              */

typedef struct position_t 
{
    unsigned int input;
    unsigned long* memory;
    struct position_t * previous;
} position;

position *
position_new(request_rec *req,
        unsigned int input, position * previous)
{
    position * p = apr_pcalloc(req->pool,sizeof(position));
    p->input = input;
    p->previous = previous;
    p->memory = 0;
    return p;
}

void
position_copyMemory(request_rec *req,position * p,unsigned long *aMemory, unsigned char aLength)
{
    unsigned char i=0;
    p->memory= (unsigned long *) apr_pcalloc(req->pool,sizeof(unsigned long) * aLength);
    if (!p->memory) {
        return;
    }
    for(i=0;i<aLength;i++)
    {
     p->memory[i]=aMemory[i];
    }
}

void
position_delete(position *p)
{
    free(p->memory);
    p->previous=0;
}

void
position_setPrevious(position * p,position * aPrevious)
{
    p->previous=aPrevious;
}

unsigned long * 
position_getMemory(position *p)
{
    return p->memory;
}

position *
position_getPrevious(position *p)
{
    return p->previous;
}

unsigned int
position_getInput(position *p)
{
    return p->input;
}


/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*                                    Noded function                                              */


typedef struct noded_t
{
    position ** Block;
    char metric;
    unsigned long positionNumber;
} noded;

noded *
noded_init(request_rec *req, char metric, unsigned long positionNumber, position ** block)
{
   noded *n = (noded *) apr_palloc(req->pool,sizeof(noded));  
    n->metric = metric;
    n->positionNumber = positionNumber;
    n->Block = block;
    return n;
}


void 
noded_addPosition(request_rec *req, noded *n,char aMetric,unsigned int aInput,unsigned long *aMemory,
        unsigned char aMemoryLength, position *aPrevious)
{
    unsigned long i;
    position ** tmp;
    position * p; 

    n->positionNumber++;
    if (n->positionNumber == 1) {
        n->Block= (position **) apr_palloc(req->pool,sizeof(position*));
    } else {
        tmp= (position **) apr_palloc(req->pool,sizeof(position*)*n->positionNumber);
        if (tmp == NULL)
            return;
        for(i=0;i<n->positionNumber-1;i++){ 
            tmp[i]=n->Block[i];
            n->Block[i]=0;
        }
        n->Block=tmp;
    }
    p = position_new(req,aInput, aPrevious);
    position_copyMemory(req,p,aMemory, aMemoryLength); 
    n->Block[n->positionNumber-1]=p;
    n->metric=aMetric;
}

void noded_replaceBlock(request_rec *req, noded *n,char aMetric,unsigned int aInput,unsigned long *aMemory,
        unsigned char aMemoryLength, position *aPrevious)
{
    unsigned long i;
    position * p;
    for(i=0;i<n->positionNumber;i++)
    {
        position_delete(n->Block[i]);
        free(n->Block[i]);
    }
    n->positionNumber=1;
    n->Block= (position **) apr_palloc(req->pool,sizeof(position*));
    if (n->Block == NULL)
        return;
    p=position_new(req,aInput,aPrevious);
    position_copyMemory(req,p,aMemory, aMemoryLength); 
    n->Block[n->positionNumber-1]=p;
    n->metric=aMetric;
}
 
char noded_isEmpty(noded* n)
{
    return ((n->positionNumber) ?1 : 0);
}

void
noded_delete(noded * n)
{
   unsigned long i;
    for(i=0;i<n->positionNumber;i++)
    {
        position_delete(n->Block[i]);
        free(n->Block[i]);
    }
    free(n->Block);
    free(n);
}

void
noded_setMetric(noded * n, char aMetric)
{
    n->metric = aMetric;
}

char  
noded_getMetric(noded *n)
{
    return n->metric;
}

unsigned long 
noded_getPositionNumber(noded *n)
{
    return n->positionNumber;
}

unsigned long * 
noded_getMemory(noded *n,unsigned long aPosition)
{
    return position_getMemory( n->Block[aPosition]);
}

position * 
noded_getAddressPosition(noded *n,unsigned long aPosition)
{
    return n->Block[aPosition]; 
}

/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*                                    Treillis function                                              */
typedef struct viterbi_t
{
    noded *** treillis;
    unsigned long long width;
    unsigned int height;
    pcc *aCode;
    char * data;
    unsigned long long dataLength;
}viterbi;

viterbi *
viterbi_init(request_rec *req, pcc *aPcc,char * aData, unsigned long long aLength)
{
    
    unsigned long long i;
    unsigned int j;
    viterbi * v = (viterbi *) apr_palloc(req->pool,sizeof(viterbi));

    if (v == NULL) 
        return NULL;

    v->aCode = aPcc;
    v->data=aData;
    v->dataLength=aLength;
    v->height=1<<(pcc_getN(v->aCode));
    v->width=floor(v->dataLength/(pcc_getN(v->aCode)));
    v->treillis = (noded ***) apr_palloc(req->pool,sizeof(noded**)*v->width+1);
    for (i=0;i<v->width+1;i++) {
        v->treillis[i] = (noded **) apr_palloc(req->pool,sizeof(noded*) * (v->height));
        for (j=0;j<v->height;j++)
            v->treillis[i][j] = 0;
    }
    return v;
}

void
viterbi_delete(viterbi * v)
{
    unsigned long long i;
    unsigned int j;
    for(i=0;i<v->width;i++) {
        for(j=0;j<v->height;j++)  
        {
            if (v->treillis[i][j] != NULL)
                noded_delete(v->treillis[i][j]);
        }
        free(v->treillis[i]);
    }
    free(v->treillis);
    free(v);
}

void
viterbi_setMetric(viterbi *v,unsigned long long x,unsigned int y, char aMetric)
{
    noded_setMetric(v->treillis[x][y],aMetric);
}

char 
viterbi_getMetric(viterbi *v,unsigned long long x, unsigned int y)
{
    return noded_getMetric(v->treillis[x][y]);
}

char
viterbi_isEmpty(viterbi *v,unsigned long long x, unsigned int y)
{
    return (v->treillis[x][y]==0);
}

unsigned int
viterbi_NumberOfPosition(viterbi *v,unsigned long long x, unsigned int y)
{
    return noded_getPositionNumber(v->treillis[x][y]);
}

unsigned long *
viterbi_getMemory(viterbi *v, unsigned long long x, unsigned int y, unsigned int aPosition)
{
    return noded_getMemory(v->treillis[x][y],aPosition);
}

position *
viterbi_getAddressPosition(viterbi *v,unsigned long long x, unsigned int y, unsigned int aPosition)
{
    return noded_getAddressPosition(v->treillis[x][y],aPosition);
}

unsigned char
distance(const char * a, unsigned int b, unsigned char length, unsigned long long begin)
{
    unsigned char i, dist=0;
    for(i=0;i<length;i++) {

        if (a[begin] != 0xF) 
            dist+=(a[begin]&0x1) ^ (b>>(length-1-i) &0x1);
        begin++;
    }
    return dist;
}

void
viterbi_addPosition(request_rec *req, viterbi *v,unsigned long long x, unsigned int y, 
        char aMetric,unsigned int aInput,
        unsigned long *aMemory, unsigned char aMemoryLength,
        position *aPrevious)
{
    if (!v->treillis[x][y]) {
         v->treillis[x][y] = noded_init(req,0,0,0);
         noded_addPosition(req, v->treillis[x][y],aMetric,aInput,aMemory,aMemoryLength, aPrevious);
    } else {
        if (viterbi_getMetric(v,x,y)>aMetric)
            noded_replaceBlock(req,v->treillis[x][y],aMetric,aInput,aMemory,aMemoryLength, aPrevious);
        else {
            if (viterbi_getMetric(v,x,y)==aMetric || viterbi_NumberOfPosition(v,x,y)==0) {
            noded_addPosition(req,v->treillis[x][y],aMetric,aInput,aMemory,aMemoryLength, aPrevious);
            }
            /*
             * else  do nothing
             */
        }
    }
}

void
viterbi_backward(request_rec *req,viterbi *v,char ** aDataDecoded, unsigned long long *dataLength )
{
    /*
     * Search the minus
     */
    char minMetric=-1, metric;
    position * tmp;
    unsigned long long i;
    unsigned long *table;
    unsigned int j, min=(1<<16)-1;

    for(j=0;j<v->height;++j) {
        if (! viterbi_isEmpty(v,v->width,j)) {
            metric=viterbi_getMetric(v,v->width,j);
            if ( (metric<minMetric) || (minMetric == -1) ) {
                min=j;
                minMetric=metric; 
            }
        }
    }
    
    if( (min == (1<<16)-1) || viterbi_isEmpty(v,v->width,min)) {
        return;
    }

    tmp=viterbi_getAddressPosition(v,v->width,min,0);
    i=v->width;
    table = (unsigned long *) apr_palloc(req->pool,sizeof(unsigned long)*(v->width));
    if (table == NULL) 
        return;

    while(tmp && i>0) {
        i--;
        table[i] = position_getInput(tmp);
        tmp=position_getPrevious(tmp);
    }

    *aDataDecoded=int_to_char(req,table,v->width,pcc_getK(v->aCode),pcc_getN(v->aCode),dataLength);
}

void
viterbi_decode(request_rec *req, viterbi * v,char ** aDataDecoded, unsigned long long *aDataDecodedLength)
{
      
    /***************/
  unsigned int mN=pcc_getN(v->aCode);
  unsigned int mK=pcc_getK(v->aCode);
  unsigned int mM=pcc_getM(v->aCode);
  unsigned long **mPoly=pcc_getPoly(v->aCode);
  /***************/

  unsigned char MaxMetric=0;
  unsigned long *nextmemory=(unsigned long *) apr_pcalloc(req->pool,sizeof(unsigned long)*mK);
    unsigned long bitmask=0;
    unsigned long positionNumber;
    unsigned char abit;
    unsigned int input;
    unsigned int value=0;
    unsigned int valueFin=0;
    unsigned long *memory;
    unsigned long long i;
    unsigned int j;
    unsigned char k;
    unsigned long m;
    unsigned int n_bit;
    unsigned char tmpdist;

    viterbi_addPosition(req,v,0,0,0,0,nextmemory,mK,0);

    for(i=0;i<mM;++i) {
        bitmask <<= 1;
        bitmask |= 1;
    }
     for(i=0;i<v->width;i++) {
        for(j=0;j<v->height;++j) {
            if (viterbi_isEmpty(v,i,j))
                continue;
            positionNumber=viterbi_NumberOfPosition(v,i,j);

            for(m=0;m<positionNumber;m++) {
                memory = viterbi_getMemory(v,i,j,m);

                for(input=0;input<((unsigned int) 1<<mK);++input) {
                   valueFin=0;
                    for(k=0;k<mK;++k) {
                        abit=input>>k & 0x1;
                        compute(mN,mM,mPoly[k],abit,memory[k],&value);
                        valueFin^=value;
                        nextmemory[k] = (memory[k]<<1 | abit) & bitmask;
                        value=0;
                    }
                    n_bit=i*mN;
                  tmpdist=distance(v->data,valueFin,mN,n_bit);
                    tmpdist+=viterbi_getMetric(v,i,j);
                    if (tmpdist<=MaxMetric)
                        viterbi_addPosition(req,v,i+1,valueFin,tmpdist,input,nextmemory,mK,viterbi_getAddressPosition(v,i,j,m));
                }
            }
        }
    }

    viterbi_backward(req,v,aDataDecoded,aDataDecodedLength);
    //viterbi_delete(v);
   // free(nextmemory);
}
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*****************************************************************************************************/
/*                                   End Pcc function                                                */


void
pcc_Code(request_rec *r,
           pcc *aCode,
           const char * data,
           unsigned long long length,
           char ** dataCoded,
           unsigned long long * codedLength)
{

    /***************/
    unsigned int mN=pcc_getN(aCode);
    unsigned int mK=pcc_getK(aCode);
    unsigned int mM=pcc_getM(aCode);
    unsigned long **mPoly=pcc_getPoly(aCode);
    unsigned long long mX0=pcc_getX0(aCode);
    unsigned char mNumLine=pcc_getNumLine(aCode);
    /***************/

    unsigned long long i;
    unsigned long j;
    unsigned int k;
    unsigned long * memory= (unsigned long *) apr_pcalloc(r->pool,sizeof(unsigned long)*mK);
    unsigned long bitmask=0;
    unsigned long long binsize;
    char * databin;
    unsigned int value=0, tmpvalue=0;
    unsigned char abit;
    char *valueFin;
    char * nstream=0;
    unsigned long long nsize;
    apr_time_t z, z1;

    for(i=0;i<mM;++i) {
        bitmask <<= 1;
        bitmask |= 1;
    }

	fprintf(stderr,"Lenght to decode %llu\n",length);
	fflush(stderr);

    char_to_bin(r,data,length,mK,mN,&databin,&binsize);
    i=0;
    valueFin=(char*)  apr_palloc(r->pool,sizeof(char)*binsize*mN);
    if  (valueFin == NULL)
        return;

    i=0;
    j=0;
    while(i<binsize) {
        for(k=0;k<mK;++k) {
            if(i>=binsize)
                break;
            abit=databin[i++]&0x1; 
            compute(mN,mM,mPoly[k],abit,memory[k],&tmpvalue);
            memory[k]=(memory[k]<<1 | abit)&bitmask;
            value^=tmpvalue;
            tmpvalue=0;
        }
        for(k=0;k<mN;k++) 
            valueFin[j++]=value>>(mN-k-1) &0x1;

        value=0;
    }

    i=0;


    pcc_puncture(r,aCode,valueFin,j,&nstream,&nsize);

    addNoise(mX0,mNumLine,nstream,nsize);

    i=0;
    bin_to_charHex(r,nstream,nsize,dataCoded,codedLength);
    
    //free(memory);
    //free(databin);
    //free(nstream);
   // free(valueFin);
}

void 
pcc_decode( request_rec *r,
           pcc *aCode,
           const char * dataCoded,
           int length,
           char ** dataDecoded,
           unsigned long long * dataLength)
{
    char * databin;
    unsigned long long binsize;
    char * dataUnpuctured;
    unsigned long long unpucturedSize;
    viterbi * v;

    pcc_debug(aCode);
    hex_to_Binchar(r,dataCoded,length,&databin,&binsize);

    addNoise(pcc_getX0(aCode),pcc_getNumLine(aCode),databin,binsize);
    pcc_unpuncture(r,aCode,databin,binsize,&dataUnpuctured,&unpucturedSize);
    
    v = viterbi_init(r,aCode,dataUnpuctured,unpucturedSize);

    viterbi_decode(r,v,dataDecoded,dataLength);
    
    //free(databin);
    //free(dataUnpuctured);

}


/*****
		ATTENTION FONCTION A MODIFIER !!
		RISQUE D'OVERFLOW IMPORTANT !!!
******/
pcc *
pcc_save(apr_pool_t * aPool,char * param)
{
    int i=0,j=0,nb_para=0;
    unsigned int s=0, t2=0,t1=0; 
    unsigned long *mPoly;
    pcc * aCode=(pcc*) apr_palloc(aPool,sizeof(pcc));
    int length = strlen(param);
    unsigned int matP;
    unsigned int nb_poly;

    if (aCode == NULL)
        return NULL;
   
    /*
     * purge
     */
    i=0;
    while(param[i]=='\n' || param[i]=='\r') 
    {
        i++;
    }
    /*
     * Read parameters n,k,m..
     */
    while(nb_para<3) {
        if(param[i]=='\0') {
            return NULL;
        }
        if(param[i]=='\r' && param[i+1]=='\n'){
            i+=2;
            switch(nb_para) {
                case 0 : 
                    aCode->mN=j;

                    break;
                case 1 : 
                    aCode->mK=j;
                    break;
                case 2 :

                    aCode->mM=j;
                    break;
                default : 
                    return NULL;
            } 
            j=0;
            nb_para++;
        } else {
            j=10*j+(param[i]-'0');
            i++;
        }
    }
    mPoly=(unsigned long*) apr_palloc(aPool,sizeof(unsigned long ) * (aCode->mK * aCode->mN));
    if (mPoly == NULL)
        return NULL;

    aCode->mPoly=(unsigned long **) apr_palloc(aPool,sizeof(unsigned long *) * aCode->mK);

    for(s=0;s<aCode->mK;s++) {
        aCode->mPoly[s]=(unsigned long*) apr_palloc(aPool,sizeof(unsigned long) * aCode->mN);
    }
    /*
     * Save polynoms
     */
    j=0;
    nb_poly=0;
    while (nb_poly<(aCode->mN*aCode->mK)) {
        if(param[i]=='\r' && param[i+1]=='\n'){
            i+=2;
            aCode->mPoly[t1][t2++]=j;
            nb_poly++;
            if(t2 == aCode->mN) {
                t2=0;
                t1++;
            }
            j=0;
        }else {
            j=10*j+(param[i]-'0');
            i++;
        }
    }

    j=0;
    while(param[i]=='\n' || param[i]=='\r') 
    {
        i++;
    }

    while(param[i]!='\r' && param[i+1]!='\n') 
    {
        j=10*j+(param[i]-'0');
        i++;
    }
    i+=2;
    aCode->mMatWidth=j;
   
    while(param[i]=='\n' || param[i]=='\r') 
    {
        i++;
    }
    aCode->mMatrix=(unsigned char *) apr_palloc(aPool,sizeof(unsigned char) *(aCode->mMatWidth*aCode->mN));
	if (aCode->mMatrix == NULL ){

	return NULL;
	}

    matP=0;
    t1=0;
    j=0;
    aCode->mMatDepth=0;
    while (matP<(aCode->mMatWidth*aCode->mN)) {
        if(param[i]=='\r' && param[i+1]=='\n'){
            i+=2;
            if (!j)
                aCode->mMatDepth +=1;
            aCode->mMatrix[t1++]=j;
            matP++;
            j=0;
        }else {
            j=10*j+(param[i]-'0');
            i++;
        }
    }

    j=0;
    while(param[i]=='\n' || param[i]=='\r') 
    {
        i++;
    }

    while(param[i]!='\r' && param[i+1]!='\n') 
    {

        j=10*j+(param[i]-'0');
        i++;
    }
    aCode->mNumLine=j;
    j=0;
    while(param[i]=='\n' || param[i]=='\r') 
    {
        i++;
    }
    while(i<length && param[i]!='\r' && param[i+1]!='\n') 
    {

        j=10*j+(param[i]-'0');
        i++;
    }
    aCode->mX0=j;
    return aCode;

}


    void 
pcc_debug(pcc* acode)
{
    unsigned int i,j;
    fprintf(stderr,"\n /*******CODE PARAMETERS ******/ \n");
    fflush(stderr);
    fprintf(stderr,"mN : %u\n",acode->mN);
    fflush(stderr);
    fprintf(stderr,"mK : %u\n",acode->mK);
    fflush(stderr);
    fprintf(stderr,"mM : %u\n",acode->mM);
    fflush(stderr);
    fprintf(stderr,"Poly : ");
    fflush(stderr);
    for (i=0; i< acode->mK;i++) {
        for (j=0; j< acode->mN; j++)
        {
            fprintf(stderr," %lu \n",acode->mPoly[i][j]);
            fflush(stderr);
        }
    }
    fprintf(stderr,"mMatWidth : %u\n",acode->mMatWidth);
    fflush(stderr);
    fprintf(stderr,"mMatDepth : %u\n",acode->mMatDepth);
    fflush(stderr);
    
    fprintf(stderr,"Matrix : ");
    fflush(stderr);
    for (i=0; i< acode->mN*acode->mMatWidth;i++)
            fprintf(stderr," %u ",acode->mMatrix[i]);

    fprintf(stderr,"\nmNumLine : %u\n",acode->mNumLine);
    fprintf(stderr,"mX0 : %llu\n",acode->mX0);
    fflush(stderr);

}
